package sceneObjects;

import main.Intersection;
import utils.*;

public class Triangle extends abstractSceneObject implements sceneObject {
    private Point3D p0, p1, p2;
    private Vector normal, N1, N2, N0;
    private double[][] invMatrix;

    public Triangle(Point3D p0, Point3D p1, Point3D p2) {
	this.p0 = p0;
	this.p1 = p1;
	this.p2 = p2;

	// Compute the inverse matrix for barycentric coordinates
	invMatrix = new double[3][3];

	double a = p0.getX();
	double d = p0.getY();
	double g = p0.getZ();

	double b = p1.getX();
	double e = p1.getY();
	double h = p1.getZ();

	double c = p2.getX();
	double f = p2.getY();
	double k = p2.getZ();

	double det = a * (e * k - f * h) + b * (f * g - k * d) + c * (d * h - e * g);
	double invDet = 1.0 / det;

	invMatrix[0][0] = invDet * (e * k - f * h);
	invMatrix[0][1] = invDet * (c * h - b * k);
	invMatrix[0][2] = invDet * (b * f - c * e);

	invMatrix[1][0] = invDet * (f * g - d * k);
	invMatrix[1][1] = invDet * (a * k - c * g);
	invMatrix[1][2] = invDet * (c * d - a * f);

	invMatrix[2][0] = invDet * (d * h - e * g);
	invMatrix[2][1] = invDet * (b * g - a * h);
	invMatrix[2][2] = invDet * (a * e - b * d);

	N0 = new Vector(p2, p0).crossProduct(new Vector(p0, p1)).normalize();
	N1 = new Vector(p0, p1).crossProduct(new Vector(p1, p2)).normalize();
	N2 = new Vector(p1, p2).crossProduct(new Vector(p2, p0)).normalize();

	// Calculate regular normal
	normal = calcNormal();

    }

    public Point3D getP0() {
	return p0;
    }

    public Point3D getP1() {
	return p1;
    }

    public Point3D getP2() {
	return p2;
    }

    public Vector getN0() {
	return N0;
    }

    public Vector getN1() {
	return N1;
    }

    public Vector getN2() {
	return N2;
    }

    private Vector calcNormal() {
	Vector v1 = new Vector(p0, p1);
	Vector v2 = new Vector(p0, p2);

	return v1.crossProduct(v2).normalize();
    }

    public Intersection getIntersect(Ray ray) {
	Point3D pPlane = ray.getPlaneIntersection(new Plane(p0, normal));

	if (pPlane == null) {
	    return null;
	}

	if (GeoUtils.isSameSide(pPlane, p0, p1, p2) && GeoUtils.isSameSide(pPlane, p2, p0, p1) && GeoUtils.isSameSide(pPlane, p1, p2, p0)) {
	    return new Intersection(pPlane.addVector(ray.getDirection().scale(-0.0001F)), this);
	} else {
	    return null;
	}
    }

    public Vector getNormalAtPoint(Intersection intersect) {
	return new Vector(normal);
    }

    public Point3D getBarycentric(Point3D E) {
	double e1 = E.getX();
	double e2 = E.getY();
	double e3 = E.getZ();

	double b1 = invMatrix[0][0] * e1 + invMatrix[0][1] * e2 + invMatrix[0][2] * e3;
	double b2 = invMatrix[1][0] * e1 + invMatrix[1][1] * e2 + invMatrix[1][2] * e3;
	double b3 = invMatrix[2][0] * e1 + invMatrix[2][1] * e2 + invMatrix[2][2] * e3;

	return new Point3D(b1, b2, b3);
    }

    public Vector getNormal() {
	return new Vector(normal);
    }
}
